package es

import (
	"context"
	"encoding/json"
	es "github.com/olivere/elastic/v7"
)

/**
es查询
*/

type UserResult struct {
	Users     []*User
	Total     int
	Page      int
	Size      int
	PageCount int
}

func buildQuery(query *UserQuery) es.Query {
	q := es.NewBoolQuery()
	if query.ID != 0 {
		q.Must(es.NewTermQuery("id", query.ID))
	}
	if len(query.Q) > 0 {
		qQuery := es.NewBoolQuery()
		qQuery.
			Should(es.NewMatchQuery("name", query.Q)).
			Should(es.NewMatchQuery("profile", query.Q)).
			MinimumNumberShouldMatch(1)

		q.Must(qQuery)
	}
	if query.MaxAge != 0 && query.MinAge != 0 {
		q.Must(es.NewRangeQuery("age").
			Gte(query.MinAge).Lt(query.MaxAge))
	}
	if len(query.BirthBegin) != 0 && len(query.BirthEnd) != 0 {
		q.Must(es.NewRangeQuery("birthday").
			Gte(query.BirthBegin).Lt(query.BirthEnd).Format("yyyy-MM-dd"))
	}

	return q
}

func getResult(page, size int, r *es.SearchResult) *UserResult {
	hits := r.Hits
	totalCount := r.TotalHits()

	searchSize := len(hits.Hits)

	pageCount := 0
	users := make([]*User, searchSize, searchSize)
	if searchSize > 0 {
		for i, hit := range hits.Hits {
			var u User
			json.Unmarshal(hit.Source, &u)
			users[i] = &u
		}
		pageCount = int(totalCount) / size
		if int(totalCount)%size != 0 {
			pageCount += 1
		}
	}

	var result = UserResult{
		Users:     users,
		Total:     int(totalCount),
		Page:      page,
		Size:      searchSize,
		PageCount: pageCount,
	}
	return &result
}

//分页查询  默认按相关性排序
func Query(query *UserQuery, page, size int) *UserResult {
	InitClient()
	q := buildQuery(query)

	resp, err := cli.Search(indexUser).From((page - 1) * size).Size(size).Query(q).Do(context.Background())
	CheckErr(err)

	return getResult(page, size, resp)
}

//分页查询  指定排序字段，此时忽略文本相关性
func QueryWithSort(query *UserQuery, page, size int, sort []string) *UserResult {
	InitClient()
	q := buildQuery(query)

	searchService := cli.Search(indexUser).
		From((page - 1) * size).
		Size(size).
		Query(q)

	//排序字段
	for _, v := range sort {
		searchService.Sort(v, false)
	}

	resp, err := searchService.Do(context.Background())
	CheckErr(err)

	return getResult(page, size, resp)
}

/**
function score query  衰减函数
*/
func FunctionScoreQuery(query *UserQuery, page, size int) *UserResult {
	InitClient()
	q := buildQuery(query)

	functionScoreQuery := es.NewFunctionScoreQuery()
	functionScoreQuery.Query(q).ScoreMode("multiply").
		AddScoreFunc(es.NewExponentialDecayFunction().
			FieldName("age").
			Origin(18).
			Offset(5).
			Scale(5)).
		AddScoreFunc(es.NewGaussDecayFunction().
			FieldName("birthday").
			Origin("2019-01-01").
			Offset("30d").
			Scale("30d"))

	searchService := cli.Search(indexUser).
		From((page - 1) * size).
		Size(size).
		Query(functionScoreQuery)

	resp, err := searchService.Do(context.Background())
	CheckErr(err)

	return getResult(page, size, resp)
}

/**
function score query  script function
*/
func FunctionScoreQuery2(query *UserQuery, page, size int) *UserResult {
	InitClient()
	q := buildQuery(query)

	scriptQuery := es.NewFunctionScoreQuery().
		Query(q).
		AddScoreFunc(es.NewFieldValueFactorFunction().Field("age").Factor(1.2)).
		AddScoreFunc(es.NewRandomFunction().Field("birthday"))

	resp, err := cli.Search(indexUser).
		From((page - 1) * size).
		Size(size).
		Query(scriptQuery).
		Do(context.Background())

	CheckErr(err)

	return getResult(page, size, resp)
}
